# -*- coding: utf-8 -*-
import copy
import datetime
import os
import random
import numpy as np
import matplotlib.pyplot as plt
import sys

# 全局变量
workpiece_num = 0  # 工件数
machine_num = 0  # 机器数
data = []  # 数据文件
LENGTH = 10  # 种群长度
POPU = []  # 种群
reference_point = [0, 0]  # 参考点
idx = 0
process_history = []
delay_history = []
AVG = 0

FILE_NAME = sys.argv[2]
TIMES = int(sys.argv[1])
OUTPATH = sys.argv[3]


def load_data(path):
    """加载数据文件到全局变量 data 中"""
    global workpiece_num, machine_num, data
    with open(path) as file:
        lines = file.readlines()
    workpiece_num, machine_num = int(lines[0]), int(lines[1])
    for i in range(3, len(lines), 3):
        id = int(lines[i].strip())  # 编号
        time = int(lines[i + 1].strip())  # 延迟时间
        process_time = np.array(lines[i + 2].strip().split(), dtype=int)  # 加工时间
        data.append([id, time, process_time])

def get_random_new():
    """生成一个随机解"""
    item = {'array': np.random.permutation(workpiece_num), 'flag': 0, 'fit': 0}
    process, delay, _ = decode(item['array'])
    item['process'] = process
    item['delay'] = delay
    return item

def init():
    """初始化种群"""
    global POPU, reference_point,AVG
    sum = 0.0
    for _ in range(LENGTH):
        item = get_random_new()
        sum += float(item['process']) / float(item['delay'])
        POPU.append(item)
    AVG = float(sum / LENGTH)
    print('AVG = ',AVG)
    for i in range(LENGTH):
        fit = 0
        x = (POPU[i]['process'], POPU[i]['delay'])
        for j in range(LENGTH):
            if i != j:
                y = (POPU[j]['process'], POPU[j]['delay'])
                fit += I(x, y)
        POPU[i]['fit'] = fit
    for item in POPU:
        process, delay, _ = decode(item['array'])
        item['process'] = process
        item['delay'] = delay
        reference_point[0] = max(reference_point[0], process)
        reference_point[1] = max(reference_point[1], delay)
    flush_flag()

def draw_gantt(X, sol):
    """画甘特图"""
    X = copy.deepcopy(X)
    times_array = copy.deepcopy([item[-1] for item in data])
    gantt = [[{'begin': 0, 'end': 0, 'length': 0, 'title': ''} for _ in range(machine_num)] for _ in range(workpiece_num)]
    color_map = {}
    for i, item in enumerate(X):
        for j, item2 in enumerate(item):
            gantt[sol[i]][j]['title'] = f'{sol[i]}-{j}'
            gantt[sol[i]][j]['end'] = item2
            gantt[sol[i]][j]['begin'] = item2 - times_array[sol[i]][j]
            gantt[sol[i]][j]['length'] = times_array[sol[i]][j]
            if gantt[sol[i]][j]['title'] not in color_map:
                color_map[gantt[sol[i]][j]['title']] = (random.random(), random.random(), random.random())
    fig, ax = plt.subplots(figsize=(10, 6))
    for i, item1 in enumerate(gantt):
        for j, item in enumerate(item1):
            ax.barh(sol[i], item['length'], left=item['begin'], height=0.4, color=color_map[item['title']])
            ax.text((item['begin'] + item['end']) / 2, sol[i], item['title'], ha='center', va='center', color='white', fontsize=5)
    ax.set_yticks(range(workpiece_num))
    plt.title(sol)
    plt.show()

def decode(X):
    """解码工件加工顺序，计算加工时间矩阵和延迟时间"""
    times_array = copy.deepcopy([item[-1] for item in data])
    sorted_arrays = [times_array[i] for i in X]
    sorted_arrays[0] = np.cumsum(sorted_arrays[0])
    delay_array = copy.deepcopy([item[-2] for item in data])
    sorted_delay = [delay_array[i] for i in X]
    delay = max(0, sorted_arrays[0][-1] - sorted_delay[0])
    for i in range(1, workpiece_num):
        for j in range(machine_num):
            if j == 0:
                sorted_arrays[i][j] += sorted_arrays[i - 1][j]
            else:
                sorted_arrays[i][j] = max(sorted_arrays[i - 1][j], sorted_arrays[i][j - 1]) + sorted_arrays[i][j]
            if j == machine_num - 1:
                delay += max(0, sorted_arrays[i][j] - sorted_delay[i])
    return np.max(sorted_arrays), delay, sorted_arrays

def I(x, y):
    """计算两个解之间的占优关系"""
    area_x = (reference_point[0] - x[0]) * (reference_point[1] - x[1] * AVG)
    area_y = (reference_point[0] - y[0]) * (reference_point[1] - y[1] * AVG)
    if (x[0] <= y[0] and x[1] * AVG <= y[1] * AVG) or (y[0] <= x[0] and y[1] * AVG <= x[1] * AVG):
        return area_x - area_y
    else:
        intersection_width = max(min(reference_point[0] - x[0], reference_point[0] - y[0]), 0)
        intersection_height = max(min(reference_point[1] - x[1] * AVG, reference_point[1] - y[1] * AVG), 0)
        intersection_area = intersection_width * intersection_height
        return area_x - intersection_area
def delfitness(index):
    global POPU
    for j,i in enumerate(POPU):
        if j != index:
            i['fit'] -= I((i['process'], i['delay']), (POPU[index]['process'], POPU[index]['delay']))
    del POPU[index]

def fitness(X):
    """计算适应度"""
    global POPU
    fit = 0
    for i in POPU:
        i['fit'] += I((i['process'], i['delay']), (X['process'], X['delay']))
        fit += I((X['process'], X['delay']), (i['process'], i['delay']))
    X['fit'] = fit
    return X

def flush_flag():
    """刷新标志位"""
    global POPU
    for i in range(LENGTH):
        for j in range(LENGTH):
            if i != j:
                if (POPU[i]['process'] > POPU[j]['process'] and POPU[i]['delay'] > POPU[j]['delay']):
                # if (POPU[i]['process'] >= POPU[j]['process'] and POPU[i]['delay'] > POPU[j]['delay']) \
                # or (POPU[i]['process'] > POPU[j]['process'] and POPU[i]['delay'] >= POPU[j]['delay']):
                    POPU[i]['flag'] = 1
                    break


def flag(X):
    """判断解是否为占优解"""
    for i in range(LENGTH):
        if X['delay'] < POPU[i]['delay'] and X['process'] < POPU[i]['process']:
        # if (X['delay'] <= POPU[i]['delay'] and X['process'] < POPU[i]['process']) \
        # or X['delay'] < POPU[i]['delay'] and X['process'] <= POPU[i]['process']:
            return True
    tempPOPU = copy.deepcopy(POPU)
    fit = 0
    for i in tempPOPU:
        i['fit'] += I((i['process'], i['delay']), (X['process'], X['delay']))
        fit += I((X['process'], X['delay']), (i['process'], i['delay']))
    for i in tempPOPU:
        if fit > i['fit']:
            return True
    return False
def repeat(X):
    for item in POPU:
        if np.array_equal(X['array'], item['array']):
            return True
    return False
def get_new(aa):
    """生成新解"""

    X = copy.deepcopy(aa)
    rand = np.random.choice(workpiece_num, 6,replace=False)
    temp = X['array'][rand[0]]
    X['array'][rand[0]] = X['array'][rand[1]]
    X['array'][rand[1]] = temp
    temp = X['array'][rand[2]]
    X['array'][rand[2]] = X['array'][rand[3]]
    X['array'][rand[3]] = temp
    temp = X['array'][rand[4]]
    X['array'][rand[4]] = X['array'][rand[5]]
    X['array'][rand[5]] = temp
    # 判断是否重复
    if repeat(X):
        get_new(X)
    process, delay, _ = decode(X['array'])
    X['process'] = process
    X['delay'] = delay
    X['fit'] = 0
    X['flag'] = 0
    return X

def get_suitable():
    """返回最适合删除的个体的下标，优先选择 flag 为 1 的个体，如果没有则选择适应值最小的个体"""
    flag_1_indices = [i for i, d in enumerate(POPU) if d['flag'] == 1]
    if flag_1_indices:
        return flag_1_indices[0]
    else:
        return min(range(len(POPU)), key=lambda i: POPU[i]['fit'])

def hamming_path(array1, array2):
    """计算汉明路径"""
    array1 = copy.deepcopy(array1)
    array2 = copy.deepcopy(array2)
    path = [array1.copy()]
    n = len(array1)
    for correct_idx, target_value in enumerate(array2):
        if array1[correct_idx] != target_value:
            target_idx = array1.index(target_value)
            array1[correct_idx], array1[target_idx] = array1[target_idx], array1[correct_idx]
            path.append(array1.copy())
    return path

def hanmin_search(X, Y):
    """执行汉明优化搜索"""
    global POPU, process_history, delay_history
    hanmin_POPU = []
    path = hamming_path(list(X['array']), list(Y['array']))
    for i in path:
        a = {}
        a['array'] = i
        hanmin_POPU.append(a)
    hanmin_l = len(hanmin_POPU)
    for index in range(1, 3000):
        i = get_new(hanmin_POPU[index % hanmin_l])
        if flag(i):
            print('汉明优化成功')
            print('汉明优化新增：', X['array'], X['process'], X['delay'])
            suitable = get_suitable()
            delfitness(suitable)
            # del POPU[suitable]
            POPU.append(fitness(i))
            print(i)
            maxI = max(range(len(POPU)), key=lambda i: POPU[i]['fit'])
            process_history.append(POPU[maxI]['process'])
            delay_history.append(POPU[maxI]['delay'])
            flush_flag()
    print('汉明优化结束')

def domain_search(times):
    """执行领域搜索"""
    global POPU, idx, process_history, delay_history
    for i in range(times):
        print('\r','【', FILE_NAME,'】',str(round(i / times * 100, 2)), '%', end="")
        X = get_new(POPU[i % LENGTH])
        index = 0
        idx += 1
        while not flag(X) and index < 100:
            index += 1
            X = get_new(POPU[i % LENGTH])
        if flag(X):
            idx = 0

            suitable = get_suitable()
            delfitness(suitable)
            # del POPU[suitable]
            X = fitness(X)
            # print(,'',end='')
            # ,'新增：', X['array'], X['process'], X['delay'], 'fit=', X['fit'])
            POPU.append(X)
            maxI = max(range(len(POPU)), key=lambda i: POPU[i]['fit'])
            process_history.append(POPU[maxI]['process'])
            delay_history.append(POPU[maxI]['delay'])
            flush_flag()
        if float(idx / times) >= 0.1:
            idx = 0
            rand = np.random.choice(LENGTH, 2,replace=False)
            hanmin_search(POPU[rand[0]], get_random_new())
            # hanmin_search(POPU[rand[0]], POPU[rand[1]])
def drawPoint():
    x = []
    y = []
    for item in POPU:
        x.append(item['process'])
        y.append(item['delay'])
    # 创建散点图
    plt.scatter(x, y)
    # 显示图形
    plt.show()
def saveToFile():
    with open(OUTPATH + FILE_NAME.replace('./data/',''), 'w') as file:
        for i in POPU:
            line = 'array = {},process = {}, delay = {}, fit = {}, flag = {}' \
                .format(i['array'],i['process'], i['delay'], i['fit'], i['flag'])
            file.write(line + '\n')
# 加载数据，初始化种群并进行领域搜索
load_data(FILE_NAME)
init()
domain_search(TIMES)
saveToFile()

# 画适应值和延迟时间的变化趋势图
# plt.plot(process_history)
# plt.plot(delay_history)
# plt.show()
